<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>父子组件的通信-父传子props</title>
</head>
<body>
<!--
■在上一个小节中,我们提到了子组件是不能引用父组件或者Vue实例的数据的。
■但是,在开发中,往往一些数据确实需要从上层传递到下层:
  口比如在一个页面中,我们从服务器请求到了很多的数据。
  口其中一部分数据,并非是我们整个页面的大组件来展示的,而是需要下面的子组件进行展示。
  口这个时候,并不会让子组件再次发送一个网络请求,而是直接让大组件(父组件)将数据传递给小组件(子组件)
■如何进行父子组件间的通信呢?
  口Vue官方提到口通过props向子组件传递数据
  口通过事件向父组件发送消息
-->

<!--
■在组件中,使用选项props来声明需要从父级接收到的数据。
■props的值有两种方式:
  口方式一:字符串数组,数组中的字符串就是传递时的名称。
  口方式二:对象,对象可以设置传递时的类型,也可以设置默认值等。
-->

<!--
■在下面的代码中,我直接将Vue实例当做父组件,并且其中包含子组件来简化代码
■真实的开发中, Vue实例和子组件的通信和父组件和子组件的通信过程是一样
-->

<div id="app">

  <!--  第一种调用方式，直接使用属性(组件对象props定义的属性，如果是驼峰命名，这里就要拆成以-拼接的名字使用)。这种方式就是直接对标签属性赋值，
        赋完值后则会直接将标签属性的值传递到组件props相应的属性中，模板中的元素使用props中的元素即可拿到相应的值-->
  <p style="color: red">未使用v-bind来绑定属性的情况：</p>
  <my-div my-title1="你好呀" my-content1="怎么的"></my-div>
  <hr>

  <!--  第二种调用方式，利用v-bind来绑定属性，则可以将父组件的data中的数据传入模板中使用，
        比如这里，绑定my-title1 和 my-content1属性，将父组件vue实例中的data属性对应的对象中相应的属性值传递到组件props相应的属性中，
        模板中的元素使用props中的元素即可拿到相应的值-->
  <p style="color: red">使用了v-bind来绑定属性的情况(数组写法)</p>
  <my-div v-bind:my-title1="title" :my-content1="content"></my-div>
  <hr>

  <p style="color: red">使用了v-bind来绑定属性的情况(对象写法--类型限制写法)</p>
  <my-div2 :my-title2="title" :my-content2="contentNumber"></my-div2>
  <hr>

  <p style="color: red">使用了v-bind来绑定属性的情况(对象写法--提供一些默认值的写法)</p>
  <my-div3 :my-foot3="foot" :my-low3="person"></my-div3>
  <!--  因为my-title3和my-content3在对象构造时对这两个属性都做了默认值返回，所以上面可以不用写这两个属性，不写就返回默认值，
        如果我们显式的写了绑定属性，也就是在标签上加上了绑定属性时必须要传值的，否则就会报错-->
  <!--  <my-div3 :my-title3="title" :my-content3="chars" :my-foot3="foot" :my-low3="person"></my-div3>-->

</div>

<!--定义组件模板-->
<template id="myDiv">
  <span>
    <!-- 使用多个属性值时，外部必须包裹一个父元素，在这里的父元素是span标签，如果外部没有包裹父元素会报错，如果只是用一个属性值，则可以不用包裹父元素-->
    <h2>{{myTitle1}}</h2>
    <p>{{myContent1}}</p>
  </span>
</template>

<template id="myDiv2">
  <span>
    <h2>{{myTitle2}}</h2>
    <p>{{myContent2}}</p>
  </span>
</template>

<template id="myDiv3">
  <span>
    <h2>{{myTitle3}}</h2>
    <p>{{myContent3}}</p>
    <p>{{myFoot3}}</p>
    <p>{{myLow3}}</p>
  </span>
</template>

<script src="../js/vue.js"></script>
<script>

  /*
    作用: props表示给组件添加标签属性，这样定义的标签属性就可以在组件标签上使用了，但是要注意一点，在定义的组件模板中使用多个属性值时，
       模板的内容必须包裹在一个父元素内，比如上面对应的myDiv组件的内容就是包裹在span标签中的，否则报错，如果只是使用一个属性，则可以不用
       包裹父元素
   */


  //【构造子组件对象（vue实例的子组件）】
  /*------------------------------------------数组写法(不需要数据验证时使用)------------------------------------------------------------*/

  //1、【数组写法】
  const myDiv = {
    template: "#myDiv",//绑定上面定义的组件模板，值时模板的id值
    props: ['myTitle1', 'myContent1']  //添加属性，给组件添加标签属性，用数组来定义多个组件标签属性，驼峰命名在标签使用时用用 - 拆分拼接的形式使用
  }

  /*------------------------------------------对象写法(需要数据验证时使用)---------------------------------------------------------------*/
  // 验证支持的类型：String、Number、Boolean、Array、Object、Date、Function、Symbol、自定义类型

  //自定义类型
  function Person(name, age) {
    this.name = name;
    this.age = age;
  }

  //2、【对象写法--类型限制写法】
  const myDiv2 = {
    template: "#myDiv2",//绑定上面定义的组件模板，值是模板的id值
    props: {
      /* 添加属性，给组件添加标签属性，用对象来定义多个组件标签属性，对象的属性名称也就是组件标签属性，
       属性名命名是驼峰命名的话，则在标签使用时用 - 拆分拼接的形式使用。
         属性对应的值是指定类型，表明这个属性只能传入对应类型的值，比如这里：myTitle1指定为字符串string类型，组件标签使用时，
       组件的my-title1属性对应的值必须都是字符串，myContent1对应的类型可能是字符串或者数字，则组件的my-content1属性对应的值可以是字符串
       或者数字类型*/
      myTitle2: String,
      myContent2: [String, Number]
    }
  }

  //2、【对象写法--提供一些默认值的写法】
  //属性包括：type、default、validator、required
  const myDiv3 = {
    template: "#myDiv3",//绑定上面定义的组件模板，值是模板的id值
    props: {
      /*添加属性，给组件添加标签属性，用对象来定义多个组件标签属性，对象的属性名称也就是组件标签属性，
        属性名命名是驼峰命名的话，则在标签使用时用 - 拆分拼接的形式使用。
        属性对应的值是对象，对象的属性指定一些限制条件，有type类型指定、default默认值值指定、validator自定义验证指定、required是否必填指定*/
      myTitle3: {
        type: String, //指定属性对应类型，这里为字符串类型
        default: "这是默认标题", //指定属性值不填时的默认值，如果值是数组或者对象的话，则必须是通过调取函数，函数返回数组或者对象
      },
      myContent3: {
        type: Array,
        default: function () { //指定属性值不填时的默认值，如果值是数组或者对象的话，则必须是通过调取函数，函数返回数组或者对象
          return ['默认内容1', '默认内容2']
        }
      },
      myFoot3: {
        type: String, //指定属性对应类型
        required: true, //指定是否必须传入值，如果指定为true，表示必须传入值，不传入就会报错，默认值为false
        validator: function (value) { //自定义验证函数，函数返回boolean值，返回true则表示通过验证，否则不通过验证
          return value.length > 5
        }
      },
      myLow3: Person //对象的写法之间是可以相互使用的，这里限制myLow3属性必须为自定义Person类型，这是上面直接类型限制的写法
    }
  }


  /*-----------------------------------------------------------------------------------------------------------------------*/
  //根组件vue实例（也相当于myDiv、myDiv2、myDiv3的父组件）
  const app = new Vue({
    el: "#app",
    data: {
      title: "标题",
      content: "内容",
      contentNumber: 190,
      foot: "内容长度必须大于5",
      person: new Person("小明", 19),
      chars: ['A', 'B']
    },
    // 【局部组件注册】
    // 注册组件，components属性，传入对象，对象的属性为组件的名称，值为构造的组件对象
    components: {
      myDiv, //注册组件，这是es6增强写法，同名可省略，相当于myDiv : myDiv
      myDiv2,
      myDiv3
    }
  });
</script>
</body>
</html>